
import ObjectUtil from './util/ObjectUtil';
import FileUtil from './util/FileUtil';
import PJSON from '../package.json';

const Fs = require('fs');
const Path = require('path');
const EXTENSION_NAME = '🔎';
/** 扩展名对应文件类型 */
const typeMap = {
    '.fire': 'scene',
    '.prefab': 'prefab',
    '.anim': 'animation',
    '.mtl': 'material',
    '.fnt.meta': 'font',
}

/** 类型对应图标 */
const iconMap = {
    'scene': '📺',
    'prefab': '🧸',
    'node': '💾',
    'component': '💿',
    'property': '🎲',
    'asset': '📦',
}
const translate = (key) => `H.${key}`;
/**
 * @en Registration method for the main process of Extension
 * @zh 为扩展的主进程的注册方法
 */
export const methods: { [key: string]: (...any: any) => any } = {
    openDebug() {
        console.log('openDebug')
        Editor.Panel.open(PJSON.name);
    },
    /**
     * @en A method that can be triggered by message
     * @zh 通过 message 触发的方法
     */
    async findCurrentSelection() {
        const uuids = Editor.Selection.getSelected('asset');
        if (uuids.length === 0) {
            console.log(`[${EXTENSION_NAME}]`, '先选择资源');
            return;
        }
        // 根据 uuid 查找
        for (let i = 0; i < uuids.length; i++) {
           await this.module.methods.findViaUuid(uuids[i]);
        }
    },
    /**
     * 使用 uuid 进行查找
     * @param {string} uuid 
     */
    async findViaUuid(uuid) {
        // 是否为有效 uuid
        if (!Editor.Utils.UUID.isUUID(uuid)) {
            console.log(`[${EXTENSION_NAME}]`, 'invalidUuid' + uuid);
            return;
        }
        // 获取资源信息
        const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', uuid);
        if (assetInfo) {
            const url = assetInfo.url.replace('db://', '');
            // 暂不查找文件夹
            if (assetInfo.type === 'folder') {
                console.log(`[${EXTENSION_NAME}]`, 'notSupportFolder' + url);
                return;
            }
            // 处理文件路径 & 打印头部日志
            const urlItems = url.split('/');
            if (!urlItems[urlItems.length - 1].includes('.')) {
                urlItems.splice(urlItems.length - 1);
            }
            console.log(`[${EXTENSION_NAME}]`, 'findAssetRefs' + urlItems.join('/'));
            // 记录子资源 uuid
            const subUuids = assetInfo ? [] : null;
            // 资源类型检查
            if (assetInfo.type === "cc.ImageAsset") {
                // 纹理子资源
                uuid = uuid+'@f9941';
            } else if (assetInfo.type === 'cc.Script') {
                // 脚本
                uuid = Editor.Utils.UUID.compressUUID(uuid, true);
            }
            // 查找
            const results = uuid ? await this.findReferences(uuid) : [];
            // Done
            this.printResult(results);
        } else {
            // 不存在的资源，直接查找 uuid
            console.log(`[${EXTENSION_NAME}]`, 'findAssetRefs' + uuid);
            const result = await this.findReferences(uuid);
            // Done
            this.printResult(result);
        }
    },
      /**
   * 获取对象中是否包含指定值以及相应属性名
   * @param {object} object 对象
   * @param {any} value 值
   * @returns {{ contains: boolean, property: string }}
   */
  getContainsInfo(object, value) {
    const result = {
      contains: false,
      property: null
    };
    // 搜索
    const search = (target, parentKey) => {
      if (ObjectUtil.isObject(target)) {
        for (const key in target) {
          if (target[key] === value) {
            result.contains = true;
            result.property = parentKey;
            return;
          }
          search(target[key], key);
        }
      } else if (Array.isArray(target)) {
        for (let i = 0, l = target.length; i < l; i++) {
          search(target[i], parentKey);
        }
      }
    }
    search(object, null);
    // Done
    return result;
  },
      /**
   * 查找节点中的引用
   * @param {object} node 目标节点
   * @param {string} uuid 查找的 uuid
   * @param {object} nodeTree 节点所在的节点树
   * @param {object[]} container 结果容器
   */
  async findRefsInNode(node, uuid, nodeTree, container) {
    // 检查节点上的组件是否有引用
    const components = node.components;
    if (components && components.length > 0) {
      for (let i = 0, l = components.length; i < l; i++) {
        const info = this.getContainsInfo(components[i], uuid);
        if (!info.contains) continue;
        // 资源类型
        let type = components[i]['__type__'];
        // 是否为脚本资源
        if (Editor.Utils.UUID.isUUID(type)) {
            const scriptUuid = Editor.Utils.UUID.decompressUUID(type),
            assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', scriptUuid);
            type = Path.basename(assetInfo.url);
        }
        // 处理属性名称
        let property = info.property;
        if (property) {
          // Label 组件需要特殊处理
          if (type === 'cc.Label' && property === '_N$file') {
            property = 'font';
          } else {
            // 去除属性名的前缀
            if (property.startsWith('_N$')) {
                property = property.replace('_N$', '');
            } else if (property[0] === '_') {
                property = property.substring(1);
            }
          }
        }
        // 保存结果
        container.push({
          node: node.path,
          component: type,
          property: property
        });
      }
    }
    // 检查预制体是否有引用
    const prefab = node.prefab;
    if (prefab) {
      // 排除预制体自己
      if (uuid !== nodeTree['__uuid__']) {
        const contains = ObjectUtil.containsValue(prefab, uuid);
        if (contains) {
          container.push({
            node: node.path
          });
        }
      }
    }
    // 遍历子节点
    const children = node.children;
    if (children && children.length > 0) {
      for (let i = 0, l = children.length; i < l; i++) {
        await  this.findRefsInNode(children[i], uuid, nodeTree, container);
      }
    }
  },
    /**
 * 查找引用
 * @param {string} uuid 
 * @returns {object[]}
 */
    async findReferences(uuid) {
        const results = [];
        /**
         * 文件处理函数
         * @param {string} filePath 文件路径
         * @param {Fs.Stats} stats 
         */
        const searchHandler = async (filePath, stats) => {
            const extname = Path.extname(filePath);
            // 场景和预制体资源
            if (extname === '.fire' || extname === '.prefab') {
                // 将资源数据转为节点树
                const nodeTree = this.getNodeTree(filePath),
                    children = nodeTree.children,
                    refs = [];
                // 遍历节点
                for (let i = 0, l = children.length; i < l; i++) {
                    await this.findRefsInNode(children[i], uuid, nodeTree, refs);
                }
                // 保存当前文件引用结果
                if (refs.length > 0) {
                    results.push({
                        type: typeMap[extname],
                        fileUrl: await Editor.Message.request('asset-db', 'query-url', filePath), //Editor.assetdb.fspathToUrl(),
                        refs: refs
                    });
                }
            }
            // 动画资源
            else if (extname === '.anim') {
                const data = JSON.parse(Fs.readFileSync(filePath)),
                    curveData = data['curveData'],
                    contains = ObjectUtil.containsValue(curveData, uuid);
                if (contains) {
                    results.push({
                        type: typeMap[extname],
                        fileUrl: await Editor.Message.request('asset-db', 'query-url', filePath)
                    });
                }
            }
            // 材质和字体资源
            else if (extname === '.mtl' || filePath.indexOf('.fnt.meta') !== -1) {
                const data = JSON.parse(Fs.readFileSync(filePath)),
                    contains = ObjectUtil.containsValue(data, uuid);
                if (contains && !(data['uuid'] === uuid)) {
                    const _extname = (extname === '.mtl') ? '.mtl' : '.fnt.meta';
                    results.push({
                        type: typeMap[_extname],
                        fileUrl: await Editor.Message.request('asset-db', 'query-url', filePath)
                    });
                }
            }
        };
        // 遍历资源目录下的文件
        const assetsPath = await Editor.Message.request('asset-db', 'query-path', 'db://assets/');
        await FileUtil.map(assetsPath, searchHandler);
        // Done
        return results;
    },
      /**
   * 获取节点树
   * @param {string} filePath 文件路径
   * @returns {object}
   */
   getNodeTree(filePath) {
    // 获取缓存
    let cache = this.cache;
    if (!cache) {
      cache = this.cache = Object.create(null);
    }
    // 从缓存中读取
    if (!cache[filePath]) {
      // 将资源数据转为节点树
      const data = JSON.parse(Fs.readFileSync(filePath));
      cache[filePath] = this.convertToNodeTree(data);
    }
    // Done
    return cache[filePath];
  },
    /**
   * 读取节点
   * @param {object} source 元数据
   * @param {number} id 节点 ID
   * @param {object} parent 父容器
   */
    convertNode(source, id, parent) {
        const node = Object.create(null),
          sourceData = source[id];
        // 基本信息
        node['__id__'] = id;
        node['_name'] = sourceData['_name'];
        node['__type__'] = sourceData['__type__'];
        // 路径
        const parentPath = parent.path || (parent['_name'] || null);
        node.path = (parentPath ? `${parentPath}/` : '') + node['_name'];
        // 组件
        const components = sourceData['_components'];
        if (components && components.length > 0) {
          const _components = node.components = [];
          for (let i = 0, l = components.length; i < l; i++) {
            const sourceComponent = source[components[i]['__id__']];
            _components.push(this.extractValidInfo(sourceComponent));
          }
        }
        // 预制体引用
        const prefab = sourceData['_prefab'];
        if (prefab) {
          const realPrefab = source[prefab['__id__']];
          node.prefab = this.extractValidInfo(realPrefab);
        }
        // 子节点
        const children = sourceData['_children'];
        if (children && children.length > 0) {
          node.children = [];
          for (let i = 0, l = children.length; i < l; i++) {
            const childId = children[i]['__id__'];
            this.convertNode(source, childId, node);
          }
        }
        // 保存数据
        parent.children.push(node);
      },
        /**
   * 提取有效信息（含有 uuid）
   * @param {object} data 元数据
   * @returns {{ __type__: string, _name: string, fileId?: string }}
   */
  extractValidInfo(data) {
    const info = Object.create(null);
    // 记录有用的属性
    const keys = ['__type__', '_name', 'fileId'];
    for (let i = 0, l = keys.length; i < l; i++) {
      const key = keys[i];
      if (data[key]) {
        info[key] = data[key];
      }
    }
    // 记录包含 uuid 的属性
    for (const key in data) {
      if (ObjectUtil.containsProperty(data[key], '__uuid__')) {
        info[key] = data[key];
      }
    }
    // Done
    return info;
  },
    /**
   * 将资源数据转为节点树
   * @param {object} source 元数据
   * @returns {object}
   */
    convertToNodeTree(source) {
        const nodeTree = Object.create(null),
          type = source[0]['__type__'];
        // 场景资源
        if (type === 'cc.SceneAsset') {
          const sceneId = source[0]['scene']['__id__'],
            children = source[sceneId]['_children'];
          nodeTree['__type__'] = 'cc.Scene';  // 类型
          nodeTree['__id__'] = sceneId;       // ID
          // 遍历节点
          nodeTree.children = [];
          for (let i = 0, l = children.length; i < l; i++) {
            const nodeId = children[i]['__id__'];
            this.convertNode(source, nodeId, nodeTree);
          }
        }
        // 预制体资源 
        else if (type === 'cc.Prefab') {
          let uuid: string | undefined = undefined;
          for (let i = source.length - 1; i >= 0; i--) {
              if (source[i]['asset']) {
                  uuid = source[i]['asset']['__uuid__'];
                  break;
              }
          }
          nodeTree['__type__'] = 'cc.Prefab'; // 类型
          nodeTree['__uuid__'] = uuid;        // uuid
          // 从根节点开始读取
          nodeTree.children = [];
          const rootId = source[0]['data']['__id__'];
          this.convertNode(source, rootId, nodeTree);
        }
        // Done
        return nodeTree;
      },
  printResult(results) {
    if (results.length === 0) {
      console.log(`[${EXTENSION_NAME}]`, 'noRefs');
      console.log(`${'----'.repeat(36)}`);
      return;
    }
    // 添加引用
    const nodeRefs = [];
    let nodeRefsCount = 0;
    const assetRefs = [];
    let assetRefsCount = 0;
    for (let i = 0, l = results.length; i < l; i++) {
      const result = results[i],
        type = result.type,
        url = result.fileUrl.replace('db://', '').replace('.meta', '');
      if (type === 'scene' || type === 'prefab') {
        nodeRefs.push(`　　　·　${iconMap[type]} [${translate(type)}] ${url}`);
        const refs = result.refs;
        for (let j = 0, n = refs.length; j < n; j++) {
          nodeRefsCount++;
          {
            const ref = refs[j];
            let item = `　　　　　　　${iconMap['node']} [${translate('node')}] ${ref.node}`;
            if (ref.component) {
              item += ` 　→ 　${iconMap['component']} [${translate('component')}] ${ref.component}`;
            }
            if (ref.property) {
              item += ` 　→ 　${iconMap['property']} [${translate('property')}] ${ref.property}`;
            }
            nodeRefs.push(item);
          }
        }
      } else {
        assetRefsCount++;
        assetRefs.push(`　　　·　${iconMap['asset']} [${translate(type)}] ${url}`);
      }
    }
    // 合并
    const texts = [`[${EXTENSION_NAME}] ${translate('searchResult')} >>>`];
    if (nodeRefs.length > 0) {
      nodeRefs.unshift(`　　　📙 ${translate('引用数')} x ${nodeRefsCount}`);
      texts.push(...nodeRefs);
    }
    if (assetRefs.length > 0) {
      assetRefs.unshift(`　　　📘 ${translate('资源引用数')} x ${assetRefsCount}`);
      texts.push(...assetRefs);
    }
    // 分隔
    texts.push(`${'----'.repeat(36)}`);
    // 打印到控制台
    if (this.expand) {
      // 逐行打印
      for (let i = 0, l = texts.length; i < l; i++) {
        console.log(texts[i]);
      }
    } else {
      // 单行打印
      const content = texts.join('\n');
      console.log(content);
    }
  }
};

/**
 * @en Method Triggered on Extension Startup
 * @zh 扩展启动时触发的方法
 */
export function load() { }

/**
 * @en Method triggered when uninstalling the extension
 * @zh 卸载扩展时触发的方法
 */
export function unload() { }
